package gbench.appdemo.ggplot.plot.panel;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;

import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Range;
import gbench.common.matlib.MatlibCanvas.XVec;
import gbench.common.matlib.data.DataReader.DFrame;
import gbench.common.matlib.img.ImageOps;
import gbench.common.matlib.img.ImageWrapper;
import gbench.common.matlib.matrix.MatrixOps;
import gbench.common.tree.LittleTree;
import gbench.common.tree.LittleTree.Elem;
import gbench.appdemo.ggplot.layout.GridLayout;
import gbench.appdemo.ggplot.layout.facet.Facet;
import gbench.appdemo.ggplot.util.TextUtil;

/**
 * 绘图框：Panel Collection 的高级版本
 * 
 * @author gbench
 *
 */
public class PanelFrame {

    /**
     * 
     * @param width  绘图板的宽度
     * @param height 绘图板的高度
     * @param af     仿射变换系数
     * @param cs     回调函数
     * @return PlotFrame
     */
    public PanelFrame addPanel(final int width, final int height, final NVec af, final Consumer<SinkPanel> cs) {
        final var panel = new SinkPanel(width, height, af);
        panels.add(panel);
        cs.accept(panel);

        return this;
    }
    
    /**
     * 覆盖机制
     * @param width 宽度
     * @param height 高度
     * @param layerdatas 层数据集合
     * @return 节点数据
     */
    public Stream<int[]> overlap_render(final int width, final int height, final ArrayList<Stream<int[]>> layerdatas) {
        final var printPanel = new SinkPanel(width, height);// 集成面板
        final var pointS = layerdatas.stream().reduce(Stream::concat).orElse(Stream.of());
        return printPanel.render(pointS);
    }
    
    /**
     * 网格打印
     * @param width 水平宽度
     * @param height 垂直高
     * @param layerdatas 层数据集合
     * @param m 行数 水平
     * @param n 列数 垂直
     * @return 节点数据
     */
    public Stream<int[]> grid_render(final int width, final int height, final ArrayList<Stream<int[]>> layerdatas, final int m, final int n) {
        final var layout = new GridLayout(width, height);// 分面布局
        final var factets = layout.facets(m, n).toArray(Facet[]::new); // 分面矩形
        final var factetPoints = layerdatas.stream().map(LittleTree.kvp_int()).flatMap(tup -> { // 分面
            final var i = tup._1();// 提取索引序号
            final var facet = factets[i];// 提取分面
            final var points = layerdatas.get(i);
            final var panelShape = this.panels.get(i).shape();// 面板尺寸，图像是花在面板上的。
            final var ratioX = facet.width() * 1d / panelShape[0];
            final var ratioY = facet.height() * 1d / panelShape[1];
            // System.out.println("ratio:" + NVec.of(ratioX, ratioY));
            
            return points.map(aa -> { // 点集合变换
                aa[0] = (int) (aa[0] * ratioX) + facet.getLt().getX();
                aa[1] = (int) (aa[1] * ratioY) + facet.getLt().getY();
                return aa;
            }); // map
            
        }); // layerdatas.stream().map

        return Stream.concat(factetPoints, // 分面
            Arrays.stream(factets).map(e -> Facet.FACET(e.getLt().offset(5, 5), e.getRb().offset(-5, -5))) // 边线
                .flatMap(e -> e.borderLines(ImageOps.rgb("fff"), 30)));
    }
    
    /**
     * 打印图像
     * 
     * @param dataframe 数据框
     * @param PlotMode 绘图模式
     * @return BufferedImage
     */
    public BufferedImage print(final DFrame dataframe, final PlotMode mode) {
        final var layerdatas = new ArrayList<Stream<int[]>>(); // 层数据
        Elem<int[]> shapes = null; // 面板形状列表
        for (final var panel : this.getPanels()) {
            shapes = Elem.cons2(panel.shape(), shapes);
            final var lines = new ArrayList<int[]>();// 文本数据行
            panel.getLayers().forEach(layer -> {
                if (layer.getData() == null) layer.setData(dataframe); //  设置默认层数据
                layer.evaluate(panel).forEach(tup -> {
                    if (tup._2().length == 3) { // 像素数据
                        panel.addPoint2(tup._1(), tup._2());
                    } else {// 文本数据
                        lines.add(tup._1().mutate(NVec::of).concat(NVec.of(tup._2())).ints());
                    } // if
                }); // 图层计算
            }); // getLayers

            layerdatas.add(Stream.concat(panel.render(),lines.stream()));// 对文本数据和像素数据进行综合处理
        } // for

        final var shapeX = shapes.data().map(NVec::new).map(NVec::data).collect(Range.aaclc(aa -> XVec.OF(MatrixOps.transpose(aa))));// shape矩阵
        final var dims = shapeX.fmap(e -> NVec.of(e).rangeA()[1].intValue()).toArray();// 提取行列最大值作为标准维度，rangeA()[1] 表示行列范围上限
        final var width = dims[0]; // 宽度水平
        final var height = dims[1];  // 高度垂直
        //System.out.println("print panel:" + NVec.of(dims));

        Stream<int[]> stream = null;
        switch (mode) {
            case GRID: { // 分面绘图
                final var n = (int)Math.ceil(Math.sqrt(getPanelCount()));// 提取列数
                final int m = (int) Math.ceil(getPanelCount()*1d/ n); // 提取行数
                //System.out.println("分面矩阵:" + m + "x" + n);
                stream = this.grid_render(width, height, layerdatas,m,n);
                break;
            } // GRID
            
            default: {// 集成面板
                stream = this.overlap_render(width, height, layerdatas);
                break;
            } // default OVERLAP
            
        }// switch

        final var matlib = new MatlibCanvas(width);// 绘图画布
        final var defaultFontFile = "C:/Windows/Fonts/simfang.ttf";
        final var textlines = new LinkedList<int[]>(); // 文本图像
        final var points = stream.filter(e -> {
            if (e.length != 5) {// 文本流
                textlines.add(e);
                return false;
            } else { // 像素点
                return true;
            } //if 
        }); // filter 数据流
        
        return matlib.plot(width, height, pen -> pen.drawPoints2(points), null, true, img->{
            final var g = new ImageWrapper(img); // 图片对象
            textlines.forEach(xyrgbzst -> { // xy rgb size style text
                final var rgb = ImageOps.rgb2clr(Arrays.copyOfRange(xyrgbzst, 2, 5)); // 获取颜色
                final var text = TextUtil.xyRgbSizeStyleText2String(xyrgbzst); // 读取文本
                g.setFont(g.getFont(defaultFontFile, xyrgbzst[6], xyrgbzst[5])).setColor(rgb)
                .drawString(text, xyrgbzst[0], xyrgbzst[1]);
            }); // forEach
        }); // plot
    }
    
    /**
     * panel的数量
     * @return
     */
    public int getPanelCount() {
        return this.getPanels().size();
    }

    /**
     * @return the panels
     */
    public List<SinkPanel> getPanels() {
        return panels;
    }

    /**
     * @param panels the panels to set
     */
    public void setPanels(List<SinkPanel> panels) {
        this.panels = panels;
    }
    
    public enum PlotMode{
        OVERLAP,GRID
    }

    private List<SinkPanel> panels = new ArrayList<SinkPanel>();
}
